//
//  instruction.c
//  PVM_emulator
//
//  Created by Pei Liu on 9/10/11.
//  Copyright 2011 __BobbyL__. All rights reserved.
//
// define all the 37 instructions

#include "instruction.h"
#include <stdlib.h>
#include <stdio.h>
#define sys_stack 500

extern int data[];
extern int reg[3];
extern int ip;
extern int* sp;

//0 palce holder
void _pvm_(int arg1, int arg2)
{
    ;
}
//1  mov     D,E     Move - copy value from E to D in data store
void _mov_(int D, int E)
{
    data[D] = data[E];
}
//2  mvi     D,N     Move immediate - put constant value N in D in data store
void _mvi_(int D, int N)
{
    data[D] = N;
}
//3  mif     D,E     Move indirect from - copy with source indirect and destination direct
void _mif_(int D, int E)
{
    int source = data[data[E]];
    data[D] = source;
}
//4  mit     D,E     Move indirect to - copy with source direct and destination indirect
void _mit_(int D, int E)
{
    int destination = data[D];
    data[destination] = data[E];
}
//5	 lri	 R, N	 Load register immediate - put constant value N in register R
void _lri_(int R, int N)
{
    reg[R] = N;
}
//6	 ldr	 R, D	 Load register - copy value from D in data store to register R 
void _ldr_(int R, int D)
{
    reg[R] = data[D];
}
//7	 str	 D, R	 Store register - copy value from register R to D in data store
void _str_(int D, int R)
{
    data[D] = reg[R];
}
//8	 mvr	 R, S	 Move register - copy value from register S to register R
void _mvr_(int R, int S)
{
    reg[R] = reg[S];
}
//9	 add	 D, E	 Add - add values in D and E in data store and leave sum in D
void _add_(int D, int E)
{
    data[D] = data[D] + data[E];
}
//10	 addri	 R, N	 Add register immediate - add constant N to value in register R
void _addri_(int R, int N)
{
    reg[R] = reg[R] + N;
}
//11	 sub	 D, E	 Subtract - subtract value in E from value in D in data store and leave difference in D
void _sub_(int D, int E)
{
    data[D] = data[D] - data[E];
}
//12	 mul	 D, E	 Multiply - multiply values in D and E in data store and leave product in D
void _mul_(int D, int E)
{
    data[D] = data[D] * data[E];
}
//13	 div	 D, E	 Divide - divide value in D by value in E in data store and leave quotient in D
void _div_(int D, int E)
{
    data[D] = (int)(data[D] / data[E]);
}
//14	 or      D, E	 Or - leave OR of Boolean values in D and E in data store in D
void _or_(int D, int E)
{
    data[D] = data[D] || data[E];
}
//15	 and	 D, E	 And - leave AND of Boolean values in D and E in data store in D
void _and_(int D, int E)
{
    data[D] = data[D] && data[E] ;
}
//16	 not	 D, 0	 Not - replace value in data store at D with its logical complement
void _not_(int D, int arg2)
{
    data[D] = !data[D];
}
//17	 b       A, 0	 Branch - transfer control to location A in code store
void _b_(int A, int arg2)
{
    ip = A - 3;
}
//18	 beq	 A, D	 Branch on equal - if value at D in data store is zero branch to A in code store
void _beq_(int A, int D)
{
    if(data[D] == 0)
    {
       ip = A - 3; 
    }
}
//19	 bne	 A, D	 Branch on not equal - if value at D in data store is not zero branch to A in code store
void _bne_(int A, int D)
{
    if (data[D] != 0) 
    {
        ip = A - 3;
    }
}
//20	 bgt	 A, D	 Branch on greater than - if value at D in data store is > 0 branch to A in code store
void _bgt_(int A, int D)
{
    if (data[D] > 0) 
    {
        ip = A - 3;
    }
}
//21	 bge	 A, D	 Branch on greater than or equal - if value at D in data store is >= 0 branch to A in code store
void _bge_(int A, int D)
{
    if (data[D] >= 0) 
    {
        ip = A - 3;
    }
}
//22	 blt	 A, D	 Branch on less than - if value at D in data store is < 0 branch to A in code store
void _blt_(int A, int D)
{
    if (data[D] < 0) 
    {
        ip = A - 3;
    }
}
//23	 ble	 A, D	 Branch on less than or equal - if value at D in data store is <= 0 branch to A in code store
void _ble_(int A, int D)
{
    if (data[D] <= 0) 
    {
        ip = A - 3;
    }
}

//24	 pushd	 D, 0	 Push data - push value at D in data store onto the stack
void _pushd_(int D, int arg2)
{
     //move stack pointer
    (*sp)--;
    
    if (sp < 0) 
    {
        fprintf(stderr, STACK_OVERFLOW_ERR);
        exit(1);
    }
    else
    {
        data[*sp] = data[D];
    }
}
//25	 pushr	 R, 0	 Push register - push value from register R onto the stack
void _pushr_(int R, int arg2)
{
     //move stack pointer
    (*sp)--;
    
    if (sp < 0) 
    {
        fprintf(stderr, STACK_OVERFLOW_ERR);
        exit(1);
    }
    else
    {
        data[*sp] = reg[R];
    }
}

//26	 pushi	 N, 0	 Push immediate - push constant N onto the stack
void _pushi_(int N, int arg2)
{
    //move stack pointer
    (*sp)--;
    
    if (sp < 0) 
    {
        fprintf(stderr, STACK_OVERFLOW_ERR);
        exit(1);
    }
    else
    {
        data[*sp] = N;
    }
}
//27	 popd	 D, 0	 Pop data - pop top of stack into data store at location D
void _popd_(int D, int arg2)
{
    if (*sp >= sys_stack) 
    {
        printf("ERR: Empty Stack.");
        exit(2);
    }
    else
    {
        data[D] = data[*sp];
    }
    
     //move stack pointer
    (*sp)++;
}
//28    popr	 R, 0	 Pop register - pop top of stack into register R
void _popr_(int R, int arg2)
{
    if (*sp >= sys_stack) 
    {
        fprintf(stderr, EMPTY_STACK_ERR);
        exit(2);
    }
    else
    {
        reg[R] = data[*sp];
    }
    
    //move stack pointer
    (*sp)++;
}

//29	 puti	 D, 0	 Put integer - display integer at D in data store
void _puti_(int D, int arg2)
{
    printf("%d", data[D]);
}
//30	 puts	 D, 0	 Put string - display NULL-terminated string beginning at D in data store
void _puts_(int D, int arg2)
{
    while (data[D] != 0) 
    {
        printf("%c", (char)data[D]);
        D++;
    }
}
//31	 line	 0, 0	 New line - move cursor to beginning of next line
void _line_(int arg1, int arg2)
{
    printf("\n");
}
//32	 geti	 D, 0	 Get integer - accept integer from keyboard and store at D in data store
void _geti_(int D, int arg2)
{
    int temp;
    scanf("%d", &temp);
    data[D] = temp;
}
//33	 gets	 D, 0	 Get string - accept string from keyboard and store NULL-terminated at D in data store
void _gets_(int D, int arg2)
{
    char temp;
    
    //clear input
    while (!((temp = getchar()) != '\n' && temp != EOF));
    
    do
    {
        data[D++] = (int)temp;
    }while((temp = getchar())!= '\n');
    
    data[D] = 0;
}
//34	 call	 A, 0	 Call - push IP and enter the subprogram at A in code store
void _call_(int A, int arg2)
{
    _pushi_(ip, 0);
    
    //cause it will add 3
    ip = A - 3;
}
//35	 ret	 N, 0	 Return - return from subprogram call removing N parameters from the stack
void _ret_(int N, int arg2)
{
    ip = data[*sp];
    (*sp)++;
    (*sp) = (*sp) + N;
}
//36	 stop	 0, 0	 Stop - end program
void _stop_(int arg1, int arg2)
{
    printf("\nEND PVM.\n");
    exit(0);
}

